Last updated: 10/18/2023
This document is used to describe the installation and use of the Mega mBot control system. Please use the following quick links to a specific section. Note that the system is developed in Python, so all examples in this document are coded in Python.
1.) Please download client.zip and server.zip, and save them to your local file system.
2.) If planning to use the desktop as the client host (not recommended, a Raspberry Pi host is prefered), a Linux based installation is required, as some modules used are not compatible with Windows and MacOS installations.
A Raspberry Pi is mounted on top of a Mega mBot. Generally speaking, server.py
runs on the Raspberry Pi and is used to control the mobility, communication, and computation of the Mega Pi. Hence, this section will show how to install Raspberry Pi OS on a new Raspberry Pi and set it up via SSH.
1.) Download a Legacy installation (Buster) of Raspberry Pi Lite OS from here. It is recommended to download Raspberry Pi OS Lite.
2.) Download balenaEtcher from here, which is OS Image Flasher for SD Cards and USB Drives.
3.) Install Raspberry Pi OS to the SD card of Raspberry Pi via balenaEtcher.
4.) Insert the SD card back into the Raspberry Pi and connect the Raspberry Pi to the internet via an ethernet cable.
5.) Check the IP of the Pi to ssh into it
$ ifconfig
6.) Connect via ssh with username pi to the IP address (e.g., 192.168.1.237) you found in step 6. The default password is raspberry.
$ ssh pi@192.168.1.237
7.) Install Python 3.5 and the libraries we will need to the Raspberry Pi:
pi@raspberrypi:~ $ sudo apt update pi@raspberrypi:~ $ sudo apt-get update && sudo apt-get upgrade -y pi@raspberrypi:~ $ sudo apt install build-essential tk-dev pi@raspberrypi:~ $ sudo apt install libncurses5-dev libncursesw5-dev libreadline6-dev pi@raspberrypi:~ $ sudo apt install libdb5.3-dev libgdbm-dev libsqlite3-dev libssl-dev pi@raspberrypi:~ $ sudo apt install libbz2-dev libexpat1-dev liblzma-dev zlib1g-dev pi@raspberrypi:~ $ sudo apt-get install libatlas-base-dev
pi@raspberrypi:~ $ wget https://www.python.org/ftp/python/3.5.3/Python-3.5.3.tgz pi@raspberrypi:~ $ tar zxvf Python-3.5.3.tgz pi@raspberrypi:~ $ cd Python-3.5.3 pi@raspberrypi:~/Python-3.5.3 $ ./configure --prefix=/usr/local/opt/python-3.5.3 pi@raspberrypi:~/Python-3.5.3 $ make -j 4 pi@raspberrypi:~/Python-3.5.3 $ sudo make altinstall pi@raspberrypi:~/Python-3.5.3 $ sudo update-alternatives –install /usr/bin/python python /home/pi/Python-3.5.3/python 3
pi@raspberrypi:~ $ sudo ln -s /usr/local/opt/python-3.5.3/bin/pydoc3.5 /usr/bin/pydoc3.5 pi@raspberrypi:~ $ sudo ln -s /usr/local/opt/python-3.5.3/bin/python3.5 /usr/bin/python3.5 pi@raspberrypi:~ $ sudo ln -s /usr/local/opt/python-3.5.3/bin/python3.5m /usr/bin/python3.5m pi@raspberrypi:~ $ sudo ln -s /usr/local/opt/python-3.5.3/bin/pyvenv-3.5 /usr/bin/pyvenv-3.5 pi@raspberrypi:~ $ sudo ln -s /usr/local/opt/python-3.5.3/bin/pip3.5 /usr/bin/pip3.5 pi@raspberrypi:~ $ sudo ln -s /usr/local/opt/python-3.5.3/bin/pip3.5 /usr/bin/pip
Note that the command for Numpy is different depending on version of Python. use the python3 command for 3, or python for 2.7.
pi@raspberrypi:~ $ sudo apt install python-numpy pi@raspberrypi:~ $ sudo apt install python3-numpy pi@raspberrypi:~ $ python3 -m pip install cython --upgrade pi@raspberrypi:~ $ pip install future-fstrings pi@raspberrypi:~ $ python2.7 -m pip install future-fstrings pi@raspberrypi:~ $ pip install https://pypi.python.org/packages/source/g/getch/getch-1.0-python2.tar.gz#md5=586ea0f1f16aa094ff6a30736ba03c50 pi@raspberrypi:~ $ pip3 install py-getch pi@raspberrypi:~ $ pip install makeblock==0.1.7 pi@raspberrypi:~ $ pip3 install makeblock==0.1.7
iPerf Network Testing
To make sure that the iPerf latency testing can be used, make sure to install the below files. Note that only 2 pis can use iPerf3 at a time, as muliple nodes are not supported.
pi@raspberrypi:~ $ wget https://downloads.es.net/pub/iperf/iperf-3.10.1.tar.gz pi@raspberrypi:~ $ tar xvf iperf-3.10.1.tar.gz iperf-3.10.1/ pi@raspberrypi:~ $ cd iperf-3.10.1 pi@raspberrypi:~/iperf-3.10.1 $ ./configure pi@raspberrypi:~/iperf-3.10.1 $ make pi@raspberrypi:~/iperf-3.10.1 $ cd .. pi@raspberrypi:~ $ sudo apt-get install lib32z1 pi@raspberrypi:~ $ sudo apt install -y iperf3 pi@raspberrypi:~ $ pip3 install iperf3
Bluetooth
pi@raspberrypi:~ $ sudo apt-get install bluetooth libbluetooth-dev pi@raspberrypi:~ $ sudo python3 -m pip install pybluez
10.) Install pip into the Python 3.x that comes with the Raspberry Pi.
pi@raspberrypi:~ $ sudo apt update pi@raspberrypi:~ $ sudo apt install python3-distutils python3-apt pi@raspberrypi:~ $ wget https://bootstrap.pypa.io/get-pip.py pi@raspberrypi:~ $ curl -O https://bootstrap.pypa.io/pip/2.7/get-pip.py pi@raspberrypi:~ $ python get-pip.py pi@raspberrypi:~ $ python -m pip install --upgrade "pip < 21.0" pi@raspberrypi:~ $ sudo apt install python3-pip pi@raspberrypi:~ $ sudo apt install python-pip
11.) Clean workspace
pi@raspberrypi:~ $ rm iperf-3.10.1.tar.gz pi@raspberrypi:~ $ rm Python-3.5.3.tar.gz pi@raspberrypi:~ $ rm get-pip.py
On the client side (desktop or laptop), you can run the client.py
as follows:
$ python3 lanclient.py Welcome to use Mega mBot control client.1). Control all Mega mBots in this network 2). Control a single Mega mBot in this network
Select from 1 to 2:
1.) The first option connects to all Mega mBots in your network, which means as long as your Mega mBot (running server.py
) is connected to the same network as your client, you can send instructions to all Mega mBots synchronously. This is useful when you want all Mega mBots to perform the same mobility moves as well as distribute computing tasks.
2.) The second option only connects to a single Mega mBot, which is useful when you want to test instructions.
Control
: this instruction allows you to enter the mobility control mode. Note that it only works for a single Mega mBot control, i.e., option 1.You can use arrow keys to control movement and the key S to exit the control mode. The key L is used to test the LEDs.exit
: this instruction allows the client to safely close the connection to the connected Mega mBot(s).evaluate
: this instruction is used to obtain a certain amount of training data based on the state of the connected Mega mBot(s).reset
: this instruction is used to delete the current mobility firmware on the connected Mega mBot(s).run
: this instruction triggers the connected Mega mBot(s) to run its installed mobility firmware to move.state
: this instruction returns the communication latency and computational latency of the connected Mega mBot(s).status
: this instruction allows connected Mega mBot(s) to send back their movement status and computational task status.stop
: this instruction stops the execution of the mobility firmware on the connected Mega mBot(s).task
: this instruction allows the client to update the mobility firmware to the connected Mega mBot(s).update
: this instruction allows the client to update the mobility firmware to the connected Mega mBot(s).upload
: this instruction allows the client to upload a new file to the connected Mega mBot(s).!(command)
: by prepending an exclamation point/bang(!) to the beginning of the instruction, you are able to execute Terminal commands (e.g., !ls -al
and !pwd
) and get the output back.On the server side (Raspberry Pi on the top of Mega mBot), you can run the server.py
as follows:
pi@raspberrypi:~/server $ sudo python3 lanserver.py Mega mBot infos: - Address: '192.168.1.150:8085' - The size of per recv in bytes: '2048' - Folder to store uploaded files: 'upload' - CPU: 'ARMv7 Processor rev 4 (v7l)' - RAM: '1Gi'Mega mBot is Waitiing for a Connection..
The config.txt
file is the configuration file that used to set up the server. The most important things in this file are the host IP address
and port
settings. Here is the default settings:
The class mBotMega
in lib/mbot.py
is the interface that provides methods to control the mobility of a Mega mBot. When you look at the code, each method is well commented for the parameters.
Lets try running an example program.
$ python client.py
Welcome to use Mega mBot control client.
1). Control all Mega mBots in this network
2). Control a single Mega mBot in this network
Select from 1 to 2: 2
Some Mega mBot nodes is pre-stored in this computer.
Do you want to rescan? (y/n): n
Which of the following Mega mBot nodes do you want to connect to?
1). '192.168.1.114:8085'
2). '192.168.1.150:8085'
Select from 1 to 2: 2
Trying to Connect to 192.168.1.150:8085
Welcome to Use Mega mBot Remote Control System
Mega mBot infos:
- Address: '192.168.1.150:8085'
- The size of per recv in bytes: '2048'
- Folder to store uploaded files: 'upload'
- CPU: 'ARMv7 Processor rev 4 (v7l)'
- RAM: '1Gi'
Instruction: update
Firmware location: mobility/test2.py
Instruction completed!
Instruction: run
The Mega mBot is now running.
Now, when you trigger the front IR proximity sensor, the Mega mBot will move backwards, and when you trigger the back impact switches, the Mega mBot will perform a different series of movements. Lastly, when you are done with the testing, you should stop the execution of this mobility firmware by:
Instruction: stop Instruction completed!Also, you can check the status of the Mega mBot to make sure that the mobility firmware is exactly stopped.
Instruction: status Current Status of Mega mBot: - Movement: The Mega mBot is not running. - Computation: The Mega mBot is not computing any task.
As you have already seen the mobility control example at the section mBotMega Module, it is possible to create custom mobility firmware to be installed at each Mega mBot. To create such firmware, you need firstly create a Mobility class in a new Python file. Once you have the Mobility class, you have to include a __init__ method that must contain exactly one parameter to which an instance of mBotMega Module will be passed. Lastly, the Mobility class must include a run method, which is executed when the client trigger the mobility firmware. In addition, if you want to import any module in the mobility firmware, you must import inside the run method. Otherwise, it won't work. Here is a sample template:
class Mobility: def __init__(self, mbot): self.mbot = mbot def run(self): from time import sleep while(1): # Your code continue here sleep(1)
Note that there are currently three(four if including ROS) methods of controlling the Mega Pi systems as a group.
Method 1 - Network Sockets
This is the simplest method to use the Mbots together, although it is slower than MPI, and a network is required, so places without a LAN network are incompatible.
sudo chgrp blutooth /var/run/sdp
Method 2 - Bluetooth Sockets
The solution to using sockets outside of a LAN network is using Bluetooth, although the downside here is that there is inevitably a minor amount of latency due to the use case of bluetooth. Usually this will be a negligible difference, but over larger data sizes, it will cause an impact. The upside is that bluetooth connection is possible without a LAN network, making it deployable anywhere.
Method 3- MPI (Message Passing Interface)
MPI, or Message Passing Interface, is the quickest of the three methods, being able to compute tasks at considerably faster times. The downside is the difficulty to program tasks in this method, as some form of knowledge of MPI is required. A network of some kind is also required
If you want to use the bluetooth server and client file you follow the steps below to install and setup the necessary blutooth parts. Using the btclient.py and btserver.py files functions identical to that of the LAN network version.
1.) Edit the /boot/config.txt
pi@raspberrypi:~ $ sudo nano /boot/config.txt
2.) At the bottom bottom and change enable-uart to 1, and add dtoverlay=pi3-miniuart-bt
3.) Reboot and check if the bluetooth is enabled to the Pi
pi@raspberrypi:~ $ cat /etc/group | grep bluetooth
3.5.) If not, add the Pi to the blutooth group
pi@raspberrypi:~ $ sudo usermod -G bluetooth -a pi pi@raspberrypi:~ $ sudo chgrp bluetooth /var/run/sdp pi@raspberrypi:~ $ sudo nano /etc/systemd/var-run-sdp.path
[Unit] Description=Monitor /var/run/sdp [Install] WantedBy=bluetooth.service [Path] PathExists=/var/run/sdp Unit=var-run-sdp.service
pi@raspberrypi:~ $ sudo nano /etc/systemd/var-run-sdp.service
[Unit] Description=Set permission of /var/run/sdp [Install] RequiredBy=var-run-sdp.path [Path] Type=simple ExecStart=/bin/chgrp bluetooth /var/run/sdp
4.5) Start the bluetooth service
pi@raspberrypi:~ $ sudo systemctl daemon-reload pi@raspberrypi:~ $ sudo systemctl enable var-run-sdp.path pi@raspberrypi:~ $ sudo systemctl enable var-run-sdp.service pi@raspberrypi:~ $ sudo systemctl start var-run-sdp.path
5.) This could break after updates, so make sure it doesn't by editting the override file as follows
pi@raspberrypi:~ $ sudo nano /etc/systemd/system/bluetooth.service.d/override.conf
[Service] ExecStart= ExecStart=/usr/lib/bluetooth/bluetoothd -C
5.5) Note that on restarts, the bluetooth may need to be restarted with the following command. This is just a quirk of the Raspberry Pi with mini-uart unfortunately
pi@raspberrypi:~ $ sudo hciconfig hci0 piscan
One of the other methods used is MPI (Message Passing Interface), for this we are using MPICH and mpi4py on the Raspberry Pi. There are plenty of good tutorials online, such as this installation. However, we will also go over it here.
pi@raspberrypi:~ $ sudo raspi-config
1.) Change the name of all Pis being used under Advanced Options. For example, we used Pi1, Pi2, Pi3,etc. You can also use "master", slave1,slave2, etc, or whatever will be easy to remember.
2.) Setup passwordless ssh so that there is no conflict when attempting to use multiple machines.
pi@master:~ $ ssh-keygen -t rsa pi@master:~ $ ssh-copy-id "other node IP"
2.5) Run ssh-copy-id for each node in your series.
3.) On each Pi, the /etc/hosts file needs to have the hostname and IP of the others, so,
pi@master:~ $ sudo nano /etc/hosts
3.5.) Add the following below the other text, where "node IP" is the IP of a Pi, and "hostname" is the name given on that Pi
"node IP" 5 spaces (tab) "hostname" 192.168.0.1 master
4.) Install MPICH and mpi4py on all Pis
pi@master:~ $ sudo apt install mpich pi@master:~ $ pip3 install mpi4py
To test that it is working correctly, use
pi@master:~ $ mpiexec -n 1 hostname
5.) Along with the host file, a machinefile is required to hold the IPs being used by mpi. This is done by simply writing the IPs or hostnames in a file titled machinefile where you want to run a program. This must be in the same folder as where you are running the mpiexec command. Alternatively, you can simply point to its location with "./foldername/machinefile"
pi@master:~ $ nano machinefile
192.168.0.1 192.168.0.2 192.168.0.3 192.168.0.4
6.) Try the hostname command with the machine file. If everything is set up properly on each Pi, the name given to each IP will be printed to the screen.
pi@master:~ $ mpiexec -n 4 -hostfile machinefile hostname
It is also possible to create custom tasks and distribute each sub-task to each connected Mega mBots and later reconstruct the result of each sub-task. To do this, there two steps:
1.) On the client side, create a function task in a new Python file. This file is used for each Mega mBot to compute the sub-task distributed to it. Hence, it must contain only one parameter, which is used to pass the data assigned to it. Once the computation is done, it must return the result, which will later be collected and reconstructed on the client (a.k.a. the master node). Similar to the run method inside the Mobility class, if you want to import any module, you must import inside this task function. Here is example of computing uncoded convolution:
def task(data): import numpy as np s = data[0] A = data[1:1+s] X = data[1+s:] rs = np.convolve(A, X) return rs
2.) On the client side again (which is also the master node), you should edit the code on ./master.py
for data decomposition, result reconstruction, and ways to get useful information of the task. This means you must have at least three functions: data_decompose, reconstruct and get_information. The first function data_decompose is used to decompose data into small piece and prepare to send to each connect Mega mBot, and it has two parameters. The number of connected Mega mBots passed by the first parameter, and the state (communication and computation latency) per Mega mBot is passed by the second parameter. The second function reconstruct is used to reconstruct results return by Mega mBots. Thus, it has only one parameter, which is used to pass the results. The get_information function is used to return useful information of the task (e.g., size of A), and it is okay to return None
if the needed information is pre-stored in data_decompose and reconstruct. The following is the example of data decomposition and result reconstruction for uncoded convolution:
import math import numpy as np def data_decompose(proc_num, state = None): size_of_A, size_of_X = (40000, 20000) s = int(math.sqrt((size_of_A * size_of_X) / proc_num)) np.random.seed(35) A = np.random.randint(1000, size=size_of_A).tolist() X = np.random.randint(1000, size=size_of_X).tolist() iter1 = int(size_of_A / s) iter2 = int(size_of_X / s) data_list = [] for i in range(0, iter1): for j in range(0, iter2): data = [s] data += A[i * s:(i + 1) * s] data += X[j * s:(j + 1) * s] data_list.append(data) return data_list def reconstruct(data_list): proc_num = len(data_list) size_of_A, size_of_X = (40000, 20000) s = int(math.sqrt((size_of_A * size_of_X) / proc_num)) iter1 = int(size_of_A / s) iter2 = int(size_of_X / s) n_A = s + size_of_X - 1 n_Z = len(data_list[0]) result = [] for i in range(0, iter1): data = [] partial_result = [0 for i in range(0, n_A)] for j in range(0, iter2): padding_left = s * j padding_right = n_A - n_Z - s * j data_temp = data_list[i*iter2+j] data_temp = np.pad(data_temp, (padding_left, padding_right), 'constant') data.append(data_temp) for d in data: partial_result = np.add(partial_result, d) result.append(partial_result) n = size_of_A + size_of_X - 1 n_Z = len(result[0]) data = [] final_result = [0 for i in range(0, n)] for j in range(0, len(result)): padding_left = s * j padding_right = n - n_Z - s * j data_temp = np.pad(result[j], (padding_left, padding_right), 'constant') data.append(data_temp) for d in data: final_result = np.add(final_result, d) # return final_result return np.round(final_result).astype(int) def get_information(): pass
Note: If the data returned by the data_decompose function is in numpy array format, be sure to convert it to a list through .tolist()
.
./task/uncoded1.py
. ./master.py
. $ python3 client.py Welcome to use Mega mBot control client.1). Control all Mega mBots in this network 2). Control a single Mega mBot in this network
Select from 1 to 2: 1 Now scan all available Mega mBots... 2022-03-15 15:12:39: Start scanning... - 192.168.1.114:8085 is available - 192.168.1.150:8085 is available
2022-03-15 15:13:05: Scan completed. Trying to Connect to 192.168.1.114:8085 Trying to Connect to 192.168.1.150:8085 Connected to all available Mega mBots. Instruction: task Task file location: task/uncoded1.py - ins to 192.168.1.114: task - ins to 192.168.1.150: task Final Result: [ 581095 863278 1330104 ... 793678 449146 109648] >> Total time (computation + communication + reconstruction): 3.90215s <<